package source;

import java.util.concurrent.Semaphore;

public class Map {

	// dimensiunea matricii(hartii)
	public static final int n = 100;

	// cat % din matrice trebuie umpluta pt a termina jocul
	private static final int percentage = 50;

	private static int fullDegree = n * n;

	private static Map map = null;

	private Construction matrice[][];

	// semafoare pt controlul numarului maxim de drone ce pot mina in acelasi
	// timp un anumit tip de resursa
	private Semaphore goldSemaphore;
	private Semaphore stoneSemaphore;
	private Semaphore woodSemaphore;
	private Semaphore foodSemaphore;

	// constructorul este private deoarece s-a aplicat patternul Singleton
	private Map(int goldMaxDrones, int stoneMaxDrones, int woodMaxDrones,
			int foodMaxDrones) {
		matrice = new Construction[n][n];

		for (int i = 0; i < n; i++)
			for (int j = 0; j < n; j++)
				matrice[i][j] = null;

		foodSemaphore = new Semaphore(foodMaxDrones);
		goldSemaphore = new Semaphore(goldMaxDrones);
		stoneSemaphore = new Semaphore(stoneMaxDrones);
		woodSemaphore = new Semaphore(woodMaxDrones);
	}

	// metoda prin care se obtine referinta unica la harta
	public synchronized static Map getInstance() {
		if (map == null)
			map = new Map(5, 5, 5, 5);
		return map;
	}

	// metodele ce simuleaza minarea, cand o drona incepe minarea scade
	// semaforul , cand acesta ajunge la 0 nu mai pot intra alte drone sa mineze

	public int mineGold(Drone drone) throws InterruptedException {

		goldSemaphore.acquire();

		drone.extractSomething();

		goldSemaphore.release();

		return 5;
	}

	public int mineStone(Drone drone) throws InterruptedException {

		stoneSemaphore.acquire();

		drone.extractSomething();

		stoneSemaphore.release();

		return 5;
	}

	public int cutWood(Drone drone) throws InterruptedException {

		woodSemaphore.acquire();

		drone.extractSomething();

		woodSemaphore.release();

		return 5;
	}

	public int gatherFood(Drone drone) throws InterruptedException {

		foodSemaphore.acquire();

		drone.extractSomething();

		foodSemaphore.release();

		return 5;
	}

	// obiect lacat ce trebuie obtinut pt a putea initializa locul pt o
	// constructie
	private Object buildLockObject = new Object();

	// metoda returneaza 1,2 sau 3 functie de starea pozitiei din matrice unde
	// se doreste construirea
	// 1=initializata/a aceluiasi player, 2=a celuilalt player, 3=construita
	public int buildHouse(int playerID, int hashCode, int pozx, int pozy) {
		synchronized (buildLockObject) {

			// daca pozitia este goala => initializeaza locatia
			if (matrice[pozx][pozy] == null) {
				fullDegree--;
				matrice[pozx][pozy] = new Construction("House", 5000, playerID,
						hashCode);

				return 1;
			}
		}

		// daca e a aceluiasi player => contribuie la constructie
		if (matrice[pozx][pozy].hasOwner(playerID)) {
			if (matrice[pozx][pozy].isBuilt())
				return 3;

			matrice[pozx][pozy].build(hashCode);

			return 1;
		}
		// daca e ocupat => nu fa nimic
		else {
			return 2;
		}
	}

	// metoda returneaza 1,2 sau 3 functie de starea pozitiei din matrice unde
	// se doreste construirea
	// 1=initializata/a aceluiasi player, 2=a celuilalt player, 3=construita
	public int buildStatue(int playerID, int hashCode, int pozx, int pozy) {
		synchronized (buildLockObject) {

			// daca pozitia este goala => initializeaza locatia
			if (matrice[pozx][pozy] == null) {
				matrice[pozx][pozy] = new Construction("Statue", 10000,
						playerID, hashCode);
				fullDegree--;
				return 1;
			}
		}

		// daca e a aceluiasi player => contribuie la constructie
		if (matrice[pozx][pozy].hasOwner(playerID)) {
			if (matrice[pozx][pozy].isBuilt())
				return 3;

			matrice[pozx][pozy].build(hashCode);

			return 1;
		}
		// daca e ocupat => nu fa nimic
		else {
			return 2;
		}
	}

	// metoda ce spune daca harta este plina in functie de procentajul stabilit
	public boolean isMapFull() {
		synchronized (buildLockObject) {
			if (fullDegree < (percentage * n * n) / 100)
				return true;

			return false;
		}
	}

	// metoda nu are rost sa fie sincronizata deoarece sigur va fi apelata dupa
	// ce toate dronele nu mai construiesc nimic
	// calculeaza scorul final si desemneaza castigatorul
	public void printPoints(int playerID1, int playerID2) {
		int p1 = 0;
		int p2 = 0;

		for (int i = 0; i < n; i++) {
			for (int j = 0; j < n; j++) {
				if (matrice[i][j] != null) {
					if (matrice[i][j].hasOwner(playerID1))
						p1 += matrice[i][j].getPoints();
					if (matrice[i][j].hasOwner(playerID2))
						p2 += matrice[i][j].getPoints();
				}
			}
		}

		System.out.println("Player_" + playerID1 + " has " + p1 + " points!!!");
		System.out.println("Player_" + playerID2 + " has " + p2 + " points!!!");

		if (p1 >= p2)
			System.out.println("Player_" + playerID1 + " has won!!");
		else
			System.out.println("Player_" + playerID2 + " has won!!");
	}
}
